package main

import (
	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
	"fmt"
	"encoding/json"
	"bytes"
	"time"
	"sync/atomic"
)

var logger = shim.NewLogger("ShimLogger")

type BaseChaincode struct {
}

//  用户信息定义
type UserInfo struct {
	UniqueID         string `json:"uniqueID"`
	CnName           string `json:"cnName"`
	EnName           string `json:"enName"`
	PhoneNumber      string `json:"phoneNumber"`
	IdcardNumber     string `json:"idcardNumber"`
	UserType         string `json:"userType"`
	CreateTime       string `json:"createTime"`
	CreateUser       string `json:"createUser"`
	LimitedDays      string `json:"limitedDays"`
	Reasons          string `json:"reasons"`
	Description      string `json:"description"`
	OtherInformation string `json:"otherInformation"`
}

// 系统信息定义
type SysInfo struct {
	Version        int `json:"version"`
	PersonCount    int `json:"personCount"`
	PersonHisCount int `json:"personHisCount"`
}

const (
	Connector     = "-"
	His_prefix    = "H" + Connector
	Key_sys_state = "sysblacklistInfo"
)

func (t *BaseChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	logger.Info("ChainCode Blacklist init...............")
	return InitSys(stub)
}

func (t *BaseChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	function, args := stub.GetFunctionAndParameters()
	logger.Info("Invoke mehtod : " + function)
	if function == "add" {
		if len(args) != 1 {
			return shim.Error("args is illegal")
		}
		return t.AddUser(stub, args[0])
	} else if function == "remove" {
		if len(args) != 2 {
			return shim.Error("args is illegal")
		}
		return t.RemoveUser(stub, args[0], args[1])
	} else if function == "update" {
		if len(args) != 3 {
			return shim.Error("args is illegal")
		}
		return t.UpdateUser(stub, args[0], args[1], args[2])
	} else if function == "query" {
		if len(args) != 1 {
			return shim.Error("args is illegal")
		}
		return t.QueryUser(stub, args[0])
	} else if function == "queryHis" {
		if len(args) != 1 {
			return shim.Error("args is illegal")
		}
		return t.QueryUserHis(stub, args[0])
	} else if function == "querySys" {
		return t.QuerySysInfo(stub)
	}
	return shim.Error("Invalid invoke function name. Expecting \"add\" \"remove\" \"query\" \"queryHis\" \"querySys\"")
}
func InitSys(stub shim.ChaincodeStubInterface) pb.Response {
	var sysInfo SysInfo
	sysInfo.Version = 1
	sysInfo.PersonCount = 0
	sysInfo.PersonHisCount = 0
	return initSysInfo(stub, sysInfo);
}
func (t *BaseChaincode) AddUser(stub shim.ChaincodeStubInterface, userInfo string) pb.Response {
	var user UserInfo
	logger.Info("input user info : " + userInfo)
	err := json.Unmarshal([]byte(userInfo), &user)
	if err != nil {
		return shim.Error("输入参数有误")
	}
	user.UniqueID = TimeUUID().String()

	logger.Info("user.PhoneNumber:" + user.PhoneNumber)
	logger.Info("user.IdcardNumber:" + user.IdcardNumber)

	err = stub.PutState(user.PhoneNumber, []byte(userInfo))
	if err != nil {
		return shim.Error("add user error")
	}
	err = stub.PutState(user.IdcardNumber, []byte(userInfo))
	if err != nil {
		return shim.Error("add user error")
	}
	t.addUserCount(stub)
	return shim.Success(nil)
}
func (t *BaseChaincode) UpdateUser(stub shim.ChaincodeStubInterface, phoneKey string, idcardKey string, userInfo string) pb.Response {
	err := stub.PutState(phoneKey, []byte(userInfo))
	if err != nil {
		return shim.Error("update user error")
	}
	err = stub.PutState(idcardKey, []byte(userInfo))
	if err != nil {
		return shim.Error("update user error")
	}
	return shim.Success([]byte("update user success"))
}
func (t *BaseChaincode) addUserTest(stub shim.ChaincodeStubInterface, phone string, idcard string, value []byte) pb.Response {
	var err error
	err = stub.PutState(phone, value)
	if err != nil {
		return shim.Error("Add user phone failed")
	}
	err = stub.PutState(idcard, value)
	if err != nil {
		return shim.Error("Add user idcard failed")
	}
	return shim.Success(nil)
}
func (t *BaseChaincode) QueryUser(stub shim.ChaincodeStubInterface, key string) pb.Response {
	result := queryUser(stub, key, key)
	return shim.Success([]byte(result))
}
func (t *BaseChaincode) RemoveUser(stub shim.ChaincodeStubInterface, phoneKey string, idcardKey string) pb.Response {
	t.addUserHis(stub, phoneKey, idcardKey, getState(stub, phoneKey))
	t.deleteState(stub, phoneKey)
	t.deleteState(stub, idcardKey)
	t.subUserAddHisCount(stub)
	return shim.Success([]byte("Remove user success"))
}
func (t *BaseChaincode) QueryUserHis(stub shim.ChaincodeStubInterface, key string) pb.Response {
	result := queryUser(stub, getKey(His_prefix, key), getKey(His_prefix, key))
	return shim.Success([]byte(result))
}
func (t *BaseChaincode) QuerySysInfo(stub shim.ChaincodeStubInterface) pb.Response {
	if b, err := json.Marshal(getSysInfo(stub)); err == nil {
		return shim.Success(b)
	}
	return shim.Error("Query sysInfo failed")
}

func main() {
	err := shim.Start(new(BaseChaincode))
	if err != nil {
		fmt.Printf("Error starting the blacklist chaincode: %s", err)
	}
}
func (t *BaseChaincode) addState(stub shim.ChaincodeStubInterface, key string, value []byte) error {
	var err error
	err = stub.PutState(key, value)
	if err != nil {
		logger.Info("add: PutState failed")
		return err
	}
	logger.Info("add: PutState success")
	return nil
}
func (t *BaseChaincode) deleteState(stub shim.ChaincodeStubInterface, key string) error {
	err := stub.DelState(key)
	if err != nil {
		logger.Info("delete: DelState failed")
		return err
	}
	logger.Info("delete: DelState success")
	return nil
}

// 增加用户
func (t *BaseChaincode) addUser(stub shim.ChaincodeStubInterface, phoneKey string, idcardKey string, value []byte) error {
	var err error
	logger.Info("add user method")
	logger.Info("add phone info")
	err = t.addState(stub, phoneKey, value)
	if err != nil {
		logger.Info("addUser phoneKey:  failed")
	}
	logger.Info("add idcard info")
	logger.Info("addUser :  success")
	return nil
}

// 查询用户
func queryUser(stub shim.ChaincodeStubInterface, phoneKey string, idcardKey string) string {
	var err error
	var userInfo UserInfo
	phoneValue := getState(stub, phoneKey)
	if phoneValue != nil {
		err = json.Unmarshal(phoneValue, &userInfo)
		if err != nil {
			err.Error();
		}
		return string(phoneValue)
	}
	idcardValue := getState(stub, idcardKey)
	if idcardValue != nil {
		err = json.Unmarshal(idcardValue, &userInfo)
		if err != nil {
			err.Error();
		}
		return string(phoneValue)
	}
	return ""
}

// 增加用户历史
func (t *BaseChaincode) addUserHis(stub shim.ChaincodeStubInterface, phoneKey string, idcardKey string, value []byte) pb.Response {
	logger.Info("add user history...")
	t.addState(stub, getKey(His_prefix, phoneKey), value)
	t.addState(stub, getKey(His_prefix, idcardKey), value)
	return shim.Success([]byte("addUserHis :  success"))
}

// 增加用户记录数
func (t *BaseChaincode) addUserCount(stub shim.ChaincodeStubInterface) pb.Response {
	logger.Info("add user count...")
	var sysInfo SysInfo
	sysInfo = getSysInfo(stub)
	oldValue := sysInfo.PersonCount + 1
	sysInfo.PersonCount = oldValue
	return t.setSysInfo(stub, sysInfo)
}

// 减少用户记录数并增加历史数
func (t *BaseChaincode) subUserAddHisCount(stub shim.ChaincodeStubInterface) {
	logger.Info("sub user count...")
	var sysInfo SysInfo
	sysInfo = getSysInfo(stub)
	oldValue := sysInfo.PersonCount - 1
	sysInfo.PersonHisCount ++
	sysInfo.PersonCount = oldValue
	t.setSysInfo(stub, sysInfo)
}

//  增加用户历史记录数
func (t *BaseChaincode) addSysHisCount(stub shim.ChaincodeStubInterface) {
	logger.Info("add userhis count...")
	var sysInfo SysInfo
	sysInfo = getSysInfo(stub)
	oldValue := sysInfo.PersonHisCount + 1
	sysInfo.PersonHisCount = oldValue
	t.setSysInfo(stub, sysInfo)
}
func (t *BaseChaincode) setSysInfo(stub shim.ChaincodeStubInterface, sysinfo SysInfo) pb.Response {
	sysinfoByte, err := json.Marshal(sysinfo)
	if err != nil {
		return shim.Error(err.Error())
	}
	err = stub.DelState(Key_sys_state)
	if err != nil {
		return shim.Error("update SysInfo error")
	}
	err = stub.PutState(Key_sys_state, sysinfoByte)
	if err != nil {
		return shim.Error("update SysInfo error")
	}
	return shim.Success([]byte("update SysInfo success"))
}

//获取系统信息
func getSysInfo(stub shim.ChaincodeStubInterface) SysInfo {
	var sysInfoJson SysInfo
	var err error
	value := getState(stub, Key_sys_state)
	err = json.Unmarshal(value, &sysInfoJson)
	if err != nil {
		err.Error();
	}
	return sysInfoJson
}

func getKey(typeString string, key string) string {
	var buf bytes.Buffer
	buf.WriteString(typeString)
	buf.WriteString(key)
	return buf.String()
}

func getState(stub shim.ChaincodeStubInterface, key string) []byte {
	var err error
	logger.Info("getState the key is " + key)
	Avalbytes, err := stub.GetState(key);
	if err != nil {
		logger.Error(err)
		return nil
	}
	if Avalbytes != nil {
		return Avalbytes
	} else {
		return nil
	}
}

func initSysInfo(stub shim.ChaincodeStubInterface, sysinfo SysInfo) pb.Response {
	sysinfoByte, err := json.Marshal(sysinfo)
	if err != nil {
		return shim.Error(err.Error())
	}
	err = stub.PutState(Key_sys_state, sysinfoByte)
	if err != nil {
		return shim.Error("init SysInfo error")
	}
	return shim.Success([]byte("init SysInfo success"))
}

type UUID [16]byte

var timeBase = time.Date(1582, time.October, 15, 0, 0, 0, 0, time.UTC).Unix()
var hardwareAddr []byte
var clockSeq uint32

func TimeUUID() UUID {
	return FromTime(time.Now())
}

func FromTime(aTime time.Time) UUID {
	var u UUID

	utcTime := aTime.In(time.UTC)
	t := uint64(utcTime.Unix()-timeBase)*10000000 + uint64(utcTime.Nanosecond()/100)
	u[0], u[1], u[2], u[3] = byte(t>>24), byte(t>>16), byte(t>>8), byte(t)
	u[4], u[5] = byte(t>>40), byte(t>>32)
	u[6], u[7] = byte(t>>56)&0x0F, byte(t>>48)

	clock := atomic.AddUint32(&clockSeq, 1)
	u[8] = byte(clock >> 8)
	u[9] = byte(clock)

	copy(u[10:], hardwareAddr)

	u[6] |= 0x10 // set version to 1 (time based example)
	u[8] &= 0x3F // clear variant
	u[8] |= 0x80 // set to IETF variant

	return u
}

func (u UUID) String() string {
	var offsets = [...]int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34}
	const hexString = "0123456789abcdef"
	r := make([]byte, 36)
	for i, b := range u {
		r[offsets[i]] = hexString[b>>4]
		r[offsets[i]+1] = hexString[b&0xF]
	}
	r[8] = '-'
	r[13] = '-'
	r[18] = '-'
	r[23] = '-'
	return string(r)
}
